package timer

import (
	"errors"
	"fmt"
	"github.com/TarsCloud/TarsGo/tars/util/rtimer"
	"reflect"
	"sync"
	"sync/atomic"
	"time"
	"xxgame.com/CommApp/Util/functor"
	"xxgame.com/CommApp/Util/goroutine"
	"xxgame.com/CommApp/Util/log"
)

var ErrTimerIsNotExist = errors.New("timer is not exist")

///////////////////// 延迟调用定时器 【简单高性能版】/////////////////////////

// 	封装 TARS-GO 时间轮算法相关接口（Tarsgo\tars\util\rtimer\timewheel.go）
// 		备注：该接口仅仅负责延迟delay时刻后调用一次func,
//			如果需要多次delay调用func, 则需要在自定义的func中
//			继续调用CallLater(delay, func)，在func中决定是否
//			继续CallLater还是终止定时调用（当然这里可用一个timer类
//			重新封装rtimer，去管理timer的启停，但是每个user再分配一个
//			timer对象再重新用 map 去维护管理，也会增加不必要的开销，
//			所以这里先用最简单方案，在自定义的func中自己维护是否继续使用定时器），
//			该版本性能高效，但只能触发开始定时器，但是无法操作主动删除指定定时器。
//
// 	delay: 需要延迟多少时刻，遵循go的时间规范，如：xxx * time.Second / time.Millisecond ...
//	fc:	   fc需要回调的函数，支持各种类型传入参数，如：func() / func(x1, ...)
/*
	************* 调用对象成员方法的定时器DEMO（支持不定参数形式） **************
	type Demo struct {}
	func (this *Demo) Func(param1 int, param2 string) {
		// do something
		// 根据逻辑判断是否需要继续调用定时器
		continueTimer := true
		if continueTimer {
			// 继续定时器
			timer.CallLater(5*time.Second, functor.New(this.Func, 99, "hello world"))
		} else {
			// do something
			// 定时器结束
		}
	}
	// 定时器调用对象方法的使用方法
	timer.CallLater(5*time.Second, functor.New((&Demo{}).Func, 99, "hello world"))

	************* 调用对象普通函数的定时器DEMO（支持不定参数形式） **************
	func Func2(param1 []int, param2 map[string]string, ...) {}
	timer.CallLater(5*time.Second, functor.New(Func, []int{1,2,3}, map[string]string{"hello":"world"}))

	************* 调用闭包的定时器DEMO （支持不定参数形式）**************
	func Func3(param1 int, param2 string, ...) {
		// ... do something ...
		timer.CallLater(5*time.Second, func() {
			// 延迟5秒调用你在这里填写的逻辑
		})
	}
*/
func CallLater(delay time.Duration, fc interface{}) {
	go func() {
		if _, ok := fc.(error); ok {
			return
		}
		<-rtimer.After(delay)
		exeCaller(fc)
	}()
}

// timer包内部使用fc函数对象执行函数
func exeCaller(fc interface{}) {
	funcTypRf := reflect.TypeOf(fc)
	if funcTypRf.Kind() == reflect.Func { // 执行闭包类型函数
		funcValRf := reflect.ValueOf(fc)
		funcValRf.Call(make([]reflect.Value, funcTypRf.NumIn()))
	} else if cf, ok := fc.(*functor.CFunctor); ok { // 调用带参的普通函数或成员函数
		cf.Call()
	}
}

////////////////////////// 封装定时器类 //////////////////////////////
/*
	调用例子：
		myTimer := timer.NewTimer(5*time.Second, -1, func() {
			// 调用相关逻辑
			do something
		})
		myTimer.Start() // 开始定时器
		myTimer.Stop()	// 停止定时器
		// 如果停止后，再调用开始，定时器相当于重新开始
		myTimer.Start()
 */

const (
	FROM_DEALINE = 0	// 定时器到期触发
	FROM_TIMER = 1		// 定时器自身调 Stop 触发
	FROM_MANAGER = 2	// 管理器调用 Stop 触发
)

type StopMsg struct {
	StopFrom int	// 1: 定时器自己触发；2：管理器触发
}

type Timer struct {
	delay      time.Duration
	cycle      int
	status     int32
	name       string
	goid       int64
	fc         interface{}
	stopNotify chan StopMsg
	timerMgr   *TimerMgr
}

// 调用NewTimer创建匿名定时器（外部需要变量引用该定时器，维护定时器的启动和停止）
// delay:	定时器间隔时间，单位可以是: 时分秒、毫秒、微妙、纳秒，看 time.Duration 定义
// cycle:	定时器执行的次数（该参数为-1时，定时器一直执行，直到调用Stop(), 它才停止执行）
// fc:		待定时器执行的函数对象，一般是普通的函数或者CFunctor对象
func NewTimer(delay time.Duration, cycle int, fc interface{}) *Timer {
	return new(Timer).Init(delay, cycle, fc).(*Timer)
}

// 定时器初始化
func (this *Timer) Init(delay time.Duration, cycle int, fc interface{}) interface{} {
	this.delay = delay
	this.cycle = cycle
	this.status = 0
	this.name = ""
	this.goid = 0
	this.fc = fc
	// 缓冲区为1的异步通讯管道
	this.stopNotify = make(chan StopMsg, 1)
	this.timerMgr = nil
	return this
}

// 定时器反初始化
func (this *Timer) UnInit() interface{} {
	this.delay = 0
	this.cycle = 0
	this.status = 0
	this.name = ""
	this.goid = 0
	this.fc = nil
	this.stopNotify = nil
	this.timerMgr = nil
	return this
}

// 定时器开始
/* 		备注1： 如果 Stop 掉定时器，再马上调用Start，定时器相当于重新开始
			但是不建议这样做，因为调用 Stop 是异步通知定时器协程停止，
			也就是说如果定时器正在执行任务的话，定时器不会马上响应停止，
			定时器会先等待任务执行完毕，再响应你的 Stop 请求，
			所以这时如果需要重新调用Start的话，因为定时器IsRunning，所以
			开启定时器失败，如果有重启定时器的需求，建议：将旧的定时器
			先调 Stop 掉，再重新创建一个新定时器，再 Start。

		备注2：定时器使用新的协程执行任务，如果待执行的定时器任务中存在
			一些公共数据，将可能产生不同协程对数据访问产生竞争，故传入
			的fc待执行的业务逻辑，需要对应开发人员关心数据是否存在竞争问题，
			如果存在数据竞争问题，则需要对应开发人员使用相关同步锁完成
			业务逻辑的访问保护（todo: 后续再看看是否有其他方案，减少开发人员关心锁的问题）
 */
func (this *Timer) Start() {
	if this.IsRunning() {
		log.TIMER.Errorf("定时器 Timer.Start() 定时器不可重复启动，" +
						"name=%+v, curGoid=%+v", this.Name(), this.goid)
		return
	}
	go func() {
		count := 0
		bForever := false
		if -1 == this.cycle {
			bForever = true
		} else {
			count = this.cycle
		}
		var stopMsg StopMsg
		atomic.StoreInt32(&this.status, 1)
		BreakTag1:
		for {
			if bForever || count > 0 {
				select {
				case <-rtimer.After(this.delay): // 定时执行函数任务
					exeCaller(this.fc)
				case stopMsg = <-this.stopNotify: // 停止定时器
					break BreakTag1
				}
			}
			if !bForever {
				count--
				if count <= 0 {
					break BreakTag1
				}
			}
		}
		atomic.StoreInt32(&this.status, 0)
		// 定时器到期或停止请求来自于管理器，若当前定时器被管理器绑定，则需要需要从管理器中删除该定时器
		if FROM_MANAGER == stopMsg.StopFrom && nil != this.TimerMgr() {
			name := this.Name()
			curGoid := this.goid
			Mgr().pool.Put(this.UnInit()) // 必须是定时器停止后才正式回收，因为回收时涉及定时器反初始化
			log.TIMER.Infof("定时器管理器 Timer.Start() 主动：回收timer成功，" +
				"name=%+v, poolNewCnt=%+v, curGoid=%+v", name, Mgr().poolNewCnt, curGoid)
		} else if FROM_DEALINE == stopMsg.StopFrom && nil != this.TimerMgr() {
			name := this.Name()
			curGoid := this.goid
			Mgr().DelTimerByName(name)
			Mgr().pool.Put(this.UnInit())
			log.TIMER.Infof("定时器管理器 Timer.Start() 到期：回收timer成功，" +
				"name=%+v, poolNewCnt=%+v, curGoid=%+v", name, Mgr().poolNewCnt, curGoid)
		}
	}()
}

// 定时器停止
/*
		备注：调用 Stop, 定时器不一定马上停止，有可能定时器正在执行任务，等它执行完毕当前任务才停止，
			这里的 Stop 是异步请求停止定时器，如果使用无缓冲区的 stopNotify，若定时器执行任务耗时过大，
			这里调用 Stop 会产生堵塞，因为定时器的协程正在执行任务，没有及时消费掉当前的停止请求，
			但是这里考虑到堵塞当前协程会影响到主业务逻辑响应客户端的速度，所以选择缓冲区为1的管道，
			让 Stop 异步执行，这时如果马上再调用 Start 的话，有可能重启定时器失败，因为定时器还
			未正式停止下来。
 */
func (this *Timer) Stop() {
	if this.IsStop() {
		return
	}
	this.stopNotify <- StopMsg{StopFrom:FROM_TIMER}
}

func (this *Timer) StopByMgr(curGoid int64) {
	if this.IsStop() {
		log.TIMER.Errorf("定时器 Timer.StopByMgr() 定时器已停止，" +
			"name=%+v, curGoid=%+v, goid=%+v", this.name, curGoid, this.goid)
		return
	}
	log.TIMER.Infof("定时器 Timer.StopByMgr() 开始发出 FROM_MANAGER 消息，" +
				"name=%+v, curGoid=%+v, goid=%+v", this.name, curGoid, this.goid)
	this.stopNotify <- StopMsg{StopFrom:FROM_MANAGER}
	log.TIMER.Infof("定时器 Timer.StopByMgr() 已经发出 FROM_MANAGER 消息，" +
				"name=%+v, curGoid=%+v, goid=%+v", this.name, curGoid, this.goid)
}

// 定时器是否正在运作中
func (this *Timer) IsRunning() bool {
	return 1 == atomic.LoadInt32(&this.status)
}

// 定时器是否已停止
func (this *Timer) IsStop() bool {
	return 0 == atomic.LoadInt32(&this.status)
}

// 获取定时器名字
// 	备注： 如果返回空字符串，则标识当前定时器为匿名定时器
func (this *Timer) Name() string {
	return this.name
}

// 设置定时器名字 ，可让匿名定时器转化成非匿名定时器
// 	备注：如果创建出的匿名定时器交给TimerMgr管理，则必须转化成非匿名定时器，否则 TimerMgr.AddTimer 不成功
// 			或者直接调用 TimerMgr.AddTimerByName，这个函数不仅可将定时器加入管理器，同时会将匿名转非匿名
func (this *Timer) SetName(name string) {
	if "" == name {
		return
	}
	if "" != this.name { // 已命名的定时器，不接受再次 SetName
		log.TIMER.Errorf("定时器 Timer.SetName() 定时器不可重复命名，name=%+v, newName=%+v, " +
			"curGoid=%+v, goid=%+v", this.name, name, goroutine.GetID(), this.goid)
		return
	}
	this.name = name
}

// 设置定时器被协程创建
func (this *Timer) SetGoid(goid int64) {
	if 0 >= goid {
		return
	}
	if 0 < this.goid {
		log.TIMER.Errorf("定时器 Timer.SetGoid() 定时器重复修改goid，"+
			"name=%+v, curGoid=%+v, goid=%+v", this.name, goroutine.GetID(), this.goid)
		return
	}
	this.goid = goid
}

// 获取定时器管理器
func (this *Timer) TimerMgr() *TimerMgr {
	return this.timerMgr
}

// 设置定时器管理器
func (this *Timer) BindTimerMgr(timerMgr *TimerMgr) {
	if nil != this.timerMgr {
		return
	}
	this.timerMgr = timerMgr
}

// 解绑定时器管理器
func (this *Timer) UnBindTimerMgr() {
	if nil == this.timerMgr {
		return
	}
	this.timerMgr = nil
}

////////////////////////// 定时器管理器 //////////////////////////////

//	** 只要定时器被管理器管理，定时器生命周期由管理器管理，开发人员无需管理
/*
	使用例子：
		使用场景一：创建并且开启一个定时器 （这种方式，定时器生命周期无需开发人员管理）
			// 方式一：开启一个新定时器并且启动定时器
			timer.Mgr().NewTimer("your_unique_timer_name", 5*time.Second, -1, func() {
				// 调用相关逻辑
				do something
			}).Start()

			// 方式二：开启一个新定时器并且启动定时器
			timer.Mgr().Start("your_unique_timer_name, 5*time.Second, -1, func() {
				// 调用相关逻辑
				do something
			})

			// 关闭并且清理一个已知定时器（这里停止就删除定时器）
			timer.Mgr().Stop("your_unique_timer_name")

		使用场景二：有需求是暂停掉运作中的定时器，希望复用该定时器，则需要先查询出定时器:
			*********************** 不建议使用，原因请下述备注 **********************
			** 备注: 不建议这种特殊用法，因为 timer.Stop() 调用，timer 依然被管理器引用
					如果不重新调用 timer.Start() 或 不调用 timer.Mgr().Stop()，
					timer 一直被管理器引用，这样会导致timer无法被正常GC垃圾回收，
					倘若慢慢增加这样定时器，无法正常释放，慢慢就会引起内存泄漏。
			timer := timer.Mgr().Find("your_unique_timer_name")
			// 停止定时器 (相当于请求暂停定时器)
			timer.Stop()
			// 启动定时器（相当于重启恢复定时器）
			timer.Start()
 */
type TimerMgr struct {
	lock sync.Mutex
	timerMgr map[string]*Timer
	pool *sync.Pool
	poolNewCnt int32
}

var initOnce sync.Once
var gTimerMgr *TimerMgr

func Mgr() *TimerMgr {
	// initOnce.Do 确保 gTimerMgr 单例
	initOnce.Do(func() {
		gTimerMgr = newTimerMgr()
		gTimerMgr.pool = &sync.Pool{
			New: func() interface{} {
				atomic.AddInt32(&gTimerMgr.poolNewCnt, 1)
				return new(Timer)
			},
		}
	})
	return gTimerMgr
}

// 创建定时器管理器（仅仅当前包使用）
func newTimerMgr() *TimerMgr {
	return new(TimerMgr).Init().(*TimerMgr)
}

// 初始化定时器管理器
func (this *TimerMgr) Init() interface{} {
	this.timerMgr = map[string]*Timer{}
	return this
}

// 添加定时器（仅支持非匿名定时器）
func (this *TimerMgr) AddTimer(timer *Timer) bool {
	name := timer.Name()
	if "" == name {
		return false
	}
	return this.AddTimerByName(name, timer, goroutine.GetID())
}

// 通过指定名字添加定时器（支持添加匿名定时器）
func (this *TimerMgr) AddTimerByName(name string, timer *Timer, goid int64) bool {
	if "" == name {
		return false
	}

	this.lock.Lock()
	defer this.lock.Unlock()

	if _, ok := this.timerMgr[name]; ok {
		return false
	}

	this.timerMgr[name] = timer
	timer.BindTimerMgr(this)
	timer.SetName(name)
	timer.SetGoid(goid)
	return true
}

// 删除定时器（仅提供给Timer类使用）
func (this *TimerMgr) delTimer(timer *Timer) error {
	name := timer.Name()
	return this.DelTimerByName(name)
}

// 通过指定名字删除定时器（仅提供给Timer类使用）
func (this *TimerMgr) DelTimerByName(name string) error {
	curGoid := goroutine.GetID()
	if "" == name {
		log.TIMER.Errorf("定时器管理器 TimerMgr.DelTimerByName() " +
										"名字为空, curGoid=%+v", curGoid)
		return errors.New("timer's name is empty")
	}
	this.lock.Lock()
	defer this.lock.Unlock()
	timer, ok := this.timerMgr[name]
	if !ok {
		log.TIMER.Errorf("定时器管理器 TimerMgr.DelTimerByName() " +
					"定时器不存在，name=%+v, curGoid=%+v", name, curGoid)
		return ErrTimerIsNotExist 
	}
	delete(this.timerMgr, name)
	timer.StopByMgr(curGoid)
	log.TIMER.Infof("定时器管理器 TimerMgr.DelTimerByName() 删除成功，name=%+v, " +
		"timerCnt=%+v, curGoid=%+v, goid=%+v", name, len(this.timerMgr), curGoid, timer.goid)
	return nil
}

// 管理器创建定时器
// 	备注：若name定时器存在，则返回旧定时器，旧定时器外部重复调用 Start 和 Stop 是安全操作，仅不生效而已
// name: 定时器名字（不可填空，否则会创建失败，TimerMgr 管理的定时器必定是非匿名定时器）
func (this *TimerMgr) NewTimer(name string, delay time.Duration, cycle int, fc interface{}) *Timer {
	if "" == name {
		return nil
	}
	//timer := NewTimer(delay, cycle, fc)
	curGoid := goroutine.GetID()
	timer := this.pool.Get().(*Timer).Init(delay, cycle, fc).(*Timer)
	if this.AddTimerByName(name, timer, curGoid) {
		log.TIMER.Infof("定时器管理器 TimerMgr.NewTimer() 创建定时器成功，name=%+v, " +
			"timerCnt=%+v, curGoid=%+v, goid=%+v", name, len(this.timerMgr), curGoid, timer.goid)
		return timer			// 返回新创建定时器
	} else {
		this.pool.Put(timer.UnInit())
		log.TIMER.Errorf("定时器管理器 TimerMgr.NewTimer() 创建定时器失败，" +
			"name=%+v, timerCtn=%+v, curGoid=%+v", name, len(this.timerMgr), curGoid)
	}
	return nil
}

// 管理器开始定时器（外部逻辑通过判断 error 的返回，可判断开启定时器是否成功）
func (this *TimerMgr) Start(name string, delay time.Duration, cycle int, fc interface{}) error {
	timer := this.Find(name)
	if nil != timer {
		log.TIMER.Errorf("定时器管理器 TimerMgr.Start() 启动失败：定时器已存在，" +
			"name=%+v, curGoid=%+v, goid=%+v", name, goroutine.GetID(), timer.goid)
		return errors.New(fmt.Sprintf("timer is exist, name=%s", name))
	}
	timer = this.NewTimer(name, delay, cycle, fc)
	if nil == timer {
		log.TIMER.Errorf("定时器管理器 TimerMgr.Start() 启动失败：创建失败，" +
								"name=%+v, curGoid=%+v", name, goroutine.GetID())
		return errors.New(fmt.Sprintf("timerMgr.NewTimer fail, name=%s", name))
	}
	timer.Start()
	log.TIMER.Infof("定时器管理器 TimerMgr.Start() 开启成功，name=%+v, poolNewCnt=%+v, " +
					"curGoid=%+v, goid=%+v", name, this.poolNewCnt, goroutine.GetID(), timer.goid)
	return nil
}

// 管理器重启定时器（外部逻辑通过判断 error 的返回，可判断开启定时器是否成功）
// 备注：如果存在原来定时器，先删除旧定时器，再开启新的定时器
func (this *TimerMgr) ReStart(name string, delay time.Duration, cycle int, fc interface{}) error {
	err := this.Remove(name)
	if nil == err || errors.Is(err, ErrTimerIsNotExist) {
		err = this.Start(name, delay, cycle, fc)
		if nil == err {
			log.TIMER.Infof("定时器管理器 TimerMgr.ReStart() 重启成功，" +
							"name=%+v, curGoid=%+v", name, goroutine.GetID())
		}
	}
	return err
}

// 管理器删除定时器（外部逻辑通过判断 error 的返回，可判断停止定时器是否成功）
func (this *TimerMgr) Stop(name string) error {
	return this.Remove(name)
}

// 管理器删除定时器（外部逻辑通过判断 error 的返回，可判断停止定时器是否成功）
func (this *TimerMgr) Remove(name string) error {
	return this.DelTimerByName(name)
}

// 查询定时器
func (this *TimerMgr) Find(name string) *Timer {
	this.lock.Lock()
	defer this.lock.Unlock()
	timer, ok := this.timerMgr[name]
	if !ok {
		return nil
	}
	return timer
}